Skip to content

fix: replace image popover with image preview dialog#965

Open
priscila-moneo wants to merge 4 commits into
masterfrom
fix/form-template-grid-image-preview
Open

fix: replace image popover with image preview dialog#965
priscila-moneo wants to merge 4 commits into
masterfrom
fix/form-template-grid-image-preview

Conversation

@priscila-moneo

@priscila-moneo priscila-moneo commented Jun 5, 2026

Copy link
Copy Markdown

ref: https://app.clickup.com/t/86ba8twjm

2nd commit:
Extended the ImagePreviewCell migration from the first commit to the remaining sponsor form item pages that had the same pattern.

image

Summary by CodeRabbit

  • New Features
    • Reusable image preview cell added across the app — shows filename, upload date, and a translated "Image Preview" title; modal shows a broken-image placeholder when loading fails.
  • Refactor
    • Replaced multiple page-specific preview implementations with the shared preview component for consistent behavior.
  • Tests
    • Added/updated tests for preview UI and image-URL validation; removed an obsolete inventory preview test suite.
  • Chores
    • Normalized allowed upload-extensions handling and added a new image-detection utility; added i18n entry for the preview title.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0457cefd-0472-431e-9b5c-501ce2791e9f

📥 Commits

Reviewing files that changed from the base of the PR and between d343b57 and 4984545.

📒 Files selected for processing (18)
  • src/actions/form-template-item-actions.js
  • src/actions/inventory-item-actions.js
  • src/actions/sponsor-forms-actions.js
  • src/app.js
  • src/components/__tests__/image-preview-cell.test.js
  • src/components/image-preview-cell.js
  • src/components/mui/PreviewModal/index.jsx
  • src/components/mui/__tests__/preview-modal.test.js
  • src/i18n/en.json
  • src/pages/sponsors-global/form-templates/add-form-template-item-popup.js
  • src/pages/sponsors-global/form-templates/form-template-item-list-page.js
  • src/pages/sponsors-global/inventory/__tests__/inventory-list-page.test.js
  • src/pages/sponsors-global/inventory/inventory-list-page.js
  • src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-add-item-from-inventory-popup.js
  • src/pages/sponsors/sponsor-form-item-list-page/index.js
  • src/pages/sponsors/sponsor-page/tabs/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js
  • src/utils/__tests__/methods.test.js
  • src/utils/methods.js
💤 Files with no reviewable changes (1)
  • src/pages/sponsors-global/inventory/tests/inventory-list-page.test.js
✅ Files skipped from review due to trivial changes (1)
  • src/actions/inventory-item-actions.js
🚧 Files skipped from review as they are similar to previous changes (12)
  • src/i18n/en.json
  • src/components/tests/image-preview-cell.test.js
  • src/app.js
  • src/utils/tests/methods.test.js
  • src/pages/sponsors/sponsor-page/tabs/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js
  • src/components/mui/tests/preview-modal.test.js
  • src/pages/sponsors-global/form-templates/form-template-item-list-page.js
  • src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-add-item-from-inventory-popup.js
  • src/pages/sponsors-global/inventory/inventory-list-page.js
  • src/utils/methods.js
  • src/components/mui/PreviewModal/index.jsx
  • src/pages/sponsors-global/form-templates/add-form-template-item-popup.js

📝 Walkthrough

Walkthrough

Adds a shared ImagePreviewCell and isImageUrl utility, refactors PreviewModal to handle image load failures with a broken-image placeholder, updates pages to use the shared preview component and requests image metadata, and adds/updates unit tests and i18n.

Changes

Image Preview Centralization

Layer / File(s) Summary
Image URL validation utility
src/utils/methods.js, src/utils/__tests__/methods.test.js, src/app.js
getFileUploadAllowedExtensions normalizes window config to an array; added isImageUrl(url) and tests covering extensions, case, query strings, and empty input; app init fallback now uses ALLOWED_INVENTORY_IMAGE_FORMATS.join(",").
PreviewModal image error handling
src/components/mui/PreviewModal/index.jsx, src/components/mui/__tests__/preview-modal.test.js, src/i18n/en.json
Refactored PreviewModal to use useState/useEffect for imageError, render a BrokenImage placeholder when url is missing or the image fails to load, reset error when opened, added preview_modal.title i18n entry, and added tests for rendering, conditional rows, close behavior, and error-state reset.
ImagePreviewCell shared component
src/components/image-preview-cell.js, src/components/__tests__/image-preview-cell.test.js
New memoized component that renders an IconButton when isImageUrl(imageUrl) is true, derives filename from fileName or decoded URL segment (query stripped, decode errors handled), opens PreviewModal with title/url/filename/uploadDate, and includes tests for visibility, dialog open/close, title, and filename resolution.
FormTemplateItemListPage integration
src/pages/sponsors-global/form-templates/form-template-item-list-page.js, src/pages/sponsors-global/form-templates/add-form-template-item-popup.js
Replaced inline icon-with-tooltip preview with ImagePreviewCell, updated imports, and removed deleteFormTemplateItem from injected Redux actions where applicable.
InventoryListPage integration
src/pages/sponsors-global/inventory/inventory-list-page.js, src/pages/sponsors-global/inventory/__tests__/inventory-list-page.test.js
Removed local InventoryImagePreviewCell and its tests; switched to shared ImagePreviewCell in the image column, added uploadDate prop, updated imports, and removed deleteInventoryItem from injected Redux actions.
Sponsor inventory popups
src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-add-item-from-inventory-popup.js, src/pages/sponsors/sponsor-form-item-list-page/index.js, src/pages/sponsors/sponsor-page/tabs/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js
Replaced per-cell tooltip/icon implementations with ImagePreviewCell, deriving preview URL from file_url or file_path and passing itemName and uploadDate.
Actions: request field updates
src/actions/form-template-item-actions.js, src/actions/inventory-item-actions.js, src/actions/sponsor-forms-actions.js
Expanded request fields/expand parameters to include images.file_url and images.created (switched to expand: "images" where applicable) so pages receive image URL and creation timestamp.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • smarcet
  • caseylocker
  • martinquiroga-exo

Poem

🐰 I hopped through code to stitch a view,

A single cell to show each picture true,
If pixels fail, a placeholder lights the way,
Shared across pages, neat and ready to play —
🖼️✨ — cheers from this little rabbit.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing image popovers with an image preview dialog across multiple pages.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/form-template-grid-image-preview

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/components/__tests__/image-preview-cell.test.js (1)

40-54: ⚡ Quick win

Add a regression test for malformed URL-encoded filenames.

Given the filename derivation path, please add a case with an invalid encoded segment (e.g., %E0%A4%A) to ensure the component doesn’t crash and still renders a fallback filename.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/__tests__/image-preview-cell.test.js` around lines 40 - 54,
Add a new test in src/components/__tests__/image-preview-cell.test.js that
renders <ImagePreviewCell> without a fileName but with an imageUrl whose last
path segment contains a malformed percent-encoding (e.g., "%E0%A4%A"), then
simulate the click on the preview button and assert the component does not crash
and shows a filename/fallback; specifically, reuse the existing test pattern
(userEvent.setup(), render(<ImagePreviewCell imageUrl={badUrl} />), await
user.click(screen.getByRole("button", { name: title }))) and assert the UI
contains either a safe fallback or the raw segment using a tolerant matcher (for
example
expect(screen.getByText(/sponsor_banner\.png|%E0%A4%A/)).toBeInTheDocument()),
so the test verifies resilience of ImagePreviewCell's filename extraction logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/image-preview-cell.js`:
- Around line 26-27: The filename decoding step for resolvedFileName can throw
on malformed percent-encodings; wrap the decodeURIComponent call that uses
imageUrl.split("/").pop().split("?")[0] in a safe guard: try to decode and on
any exception fall back to the raw segment (or a sanitized fallback like an
empty string); update the expression that assigns resolvedFileName (refer to
resolvedFileName, imageUrl) to use this safe decode logic so render-time errors
are avoided.

In `@src/pages/sponsors-global/form-templates/form-template-item-list-page.js`:
- Around line 209-213: The render callback currently destructures file_url from
row.images[0] which can throw if the first array element is nullish; update the
render implementation used for the column to read the URL via optional chaining
(e.g., access row.images?.[0]?.file_url) before calling isImageUrl, and only
return <ImagePreviewCell imageUrl={url} /> when url is a valid string and
isImageUrl(url) is true; keep the checks around row.images length/existence and
use the same symbols (render, row.images, isImageUrl, ImagePreviewCell) so the
change is localized to that render function.

In `@src/utils/methods.js`:
- Around line 637-641: getFileUploadAllowedExtensions currently returns raw
tokens without trimming, so inputs like "jpg, png,   " produce entries with
whitespace; update the function (getFileUploadAllowedExtensions) to normalize
tokens by mapping each element (whether coming from an array or from
String(ext).split(",")) through .toString()/String(...) then .trim(), and then
filter(Boolean) to remove empty strings—i.e., convert ext to an array, trim
every item, and filter out falsy values before returning.

---

Nitpick comments:
In `@src/components/__tests__/image-preview-cell.test.js`:
- Around line 40-54: Add a new test in
src/components/__tests__/image-preview-cell.test.js that renders
<ImagePreviewCell> without a fileName but with an imageUrl whose last path
segment contains a malformed percent-encoding (e.g., "%E0%A4%A"), then simulate
the click on the preview button and assert the component does not crash and
shows a filename/fallback; specifically, reuse the existing test pattern
(userEvent.setup(), render(<ImagePreviewCell imageUrl={badUrl} />), await
user.click(screen.getByRole("button", { name: title }))) and assert the UI
contains either a safe fallback or the raw segment using a tolerant matcher (for
example
expect(screen.getByText(/sponsor_banner\.png|%E0%A4%A/)).toBeInTheDocument()),
so the test verifies resilience of ImagePreviewCell's filename extraction logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 737d8ee1-de60-4ff6-92d2-78089fe1cd24

📥 Commits

Reviewing files that changed from the base of the PR and between 8243964 and a35504f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (10)
  • src/components/__tests__/image-preview-cell.test.js
  • src/components/image-preview-cell.js
  • src/components/mui/PreviewModal/index.jsx
  • src/components/mui/__tests__/preview-modal.test.js
  • src/i18n/en.json
  • src/pages/sponsors-global/form-templates/form-template-item-list-page.js
  • src/pages/sponsors-global/inventory/__tests__/inventory-list-page.test.js
  • src/pages/sponsors-global/inventory/inventory-list-page.js
  • src/utils/__tests__/methods.test.js
  • src/utils/methods.js
💤 Files with no reviewable changes (1)
  • src/pages/sponsors-global/inventory/tests/inventory-list-page.test.js

Comment thread src/components/image-preview-cell.js Outdated
Comment thread src/pages/sponsors-global/form-templates/form-template-item-list-page.js Outdated
Comment thread src/utils/methods.js

useEffect(() => {
if (open) setImageError(false);
}, [open, url]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

url shouldn't be a dependecy here

) : null
render: (row) => {
const url = row.images?.[0]?.file_url ?? row.images?.[0]?.file_path;
if (!url || !isImageUrl(url)) return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just put this inside ?

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/sponsors-global/inventory/inventory-list-page.js (1)

172-180: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing file_path fallback inconsistency.

Lines 148-152, 147-151, and 162-166 in the other files changed by this PR all use the pattern row.images?.[0]?.file_url ?? row.images?.[0]?.file_path to fall back to file_path when file_url is unavailable. This file only checks file_url, which may prevent images from displaying when file_url is missing but file_path exists.

🔧 Suggested fix to align with other files
     render: (row) => {
       const hasImages = Array.isArray(row.images) && row.images.length > 0;
-      const imageUrl = row.images?.[0]?.file_url;
+      const imageUrl = row.images?.[0]?.file_url ?? row.images?.[0]?.file_path;
       const itemName = row.name;

       if (!hasImages || !imageUrl) return null;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/sponsors-global/inventory/inventory-list-page.js` around lines 172
- 180, The render function for the image cell currently only reads
row.images?.[0]?.file_url which can be undefined even when
row.images?.[0]?.file_path exists; update the imageUrl assignment in the render
(the variable used when rendering ImagePreviewCell) to use a fallback: imageUrl
= row.images?.[0]?.file_url ?? row.images?.[0]?.file_path, and adjust the
hasImages/imageUrl existence check so the ImagePreviewCell is rendered when
either URL or file_path is present while keeping itemName unchanged.
🧹 Nitpick comments (1)
src/pages/sponsors-global/inventory/inventory-list-page.js (1)

173-177: 💤 Low value

Redundant hasImages check.

The hasImages check is unnecessary because imageUrl already uses optional chaining (row.images?.[0]?.file_url). If row.images is not an array or is empty, imageUrl will be undefined, which is caught by the !imageUrl condition.

♻️ Optional simplification
     render: (row) => {
-      const hasImages = Array.isArray(row.images) && row.images.length > 0;
       const imageUrl = row.images?.[0]?.file_url ?? row.images?.[0]?.file_path;
       const itemName = row.name;

-      if (!hasImages || !imageUrl) return null;
+      if (!imageUrl) return null;

       return <ImagePreviewCell imageUrl={imageUrl} itemName={itemName} />;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/sponsors-global/inventory/inventory-list-page.js` around lines 173
- 177, The hasImages boolean is redundant because imageUrl already uses optional
chaining; remove the hasImages declaration and simplify the guard to only check
imageUrl (i.e., drop the Array.isArray(row.images) && row.images.length > 0
check and keep the existing imageUrl logic). Locate the block using variables
hasImages, imageUrl, and itemName in inventory-list-page.js and delete the
hasImages line and the combined if (!hasImages || !imageUrl) guard, replacing it
with a single if (!imageUrl) return null to preserve behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/pages/sponsors-global/inventory/inventory-list-page.js`:
- Around line 172-180: The render function for the image cell currently only
reads row.images?.[0]?.file_url which can be undefined even when
row.images?.[0]?.file_path exists; update the imageUrl assignment in the render
(the variable used when rendering ImagePreviewCell) to use a fallback: imageUrl
= row.images?.[0]?.file_url ?? row.images?.[0]?.file_path, and adjust the
hasImages/imageUrl existence check so the ImagePreviewCell is rendered when
either URL or file_path is present while keeping itemName unchanged.

---

Nitpick comments:
In `@src/pages/sponsors-global/inventory/inventory-list-page.js`:
- Around line 173-177: The hasImages boolean is redundant because imageUrl
already uses optional chaining; remove the hasImages declaration and simplify
the guard to only check imageUrl (i.e., drop the Array.isArray(row.images) &&
row.images.length > 0 check and keep the existing imageUrl logic). Locate the
block using variables hasImages, imageUrl, and itemName in
inventory-list-page.js and delete the hasImages line and the combined if
(!hasImages || !imageUrl) guard, replacing it with a single if (!imageUrl)
return null to preserve behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 01456fa8-7e55-4f51-b543-2d94a737e62f

📥 Commits

Reviewing files that changed from the base of the PR and between a35504f and d343b57.

📒 Files selected for processing (9)
  • src/app.js
  • src/components/image-preview-cell.js
  • src/components/mui/PreviewModal/index.jsx
  • src/pages/sponsors-global/form-templates/add-form-template-item-popup.js
  • src/pages/sponsors-global/form-templates/form-template-item-list-page.js
  • src/pages/sponsors-global/inventory/inventory-list-page.js
  • src/pages/sponsors/sponsor-form-item-list-page/components/sponsor-form-add-item-from-inventory-popup.js
  • src/pages/sponsors/sponsor-page/tabs/sponsor-forms-tab/components/manage-items/sponsor-form-item-from-inventory.js
  • src/utils/methods.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/utils/methods.js
  • src/pages/sponsors-global/form-templates/form-template-item-list-page.js
  • src/components/image-preview-cell.js
  • src/components/mui/PreviewModal/index.jsx

@santipalenque santipalenque left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Signed-off-by: Priscila Moneo <priscila_moneo@hotmail.com.ar>
Signed-off-by: Priscila Moneo <priscila_moneo@hotmail.com.ar>
@priscila-moneo priscila-moneo force-pushed the fix/form-template-grid-image-preview branch 2 times, most recently from 4cea787 to ce3f134 Compare June 9, 2026 13:47
Signed-off-by: Priscila Moneo <priscila_moneo@hotmail.com.ar>
@priscila-moneo priscila-moneo force-pushed the fix/form-template-grid-image-preview branch from ce3f134 to e2edcf0 Compare June 9, 2026 13:51
</Tooltip>
) : null
render: (row) => {
const url = row.images?.[0]?.file_url;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent URL resolution compared to the other migrated consumers. add-form-template-item-popup.js and both sponsor pages use file_url ?? file_path as a fallback, but this file only reads file_url. If any inventory item has file_path set but not file_url, those list rows will silently show no preview icon while the popup pages will show one for the same item.

Consider applying the same fallback here:

const url = row.images?.[0]?.file_url ?? row.images?.[0]?.file_path;

Or, if file_url is always guaranteed to be populated for inventory images, remove the ?? file_path fallback from the popup consumers to make the intent explicit.

Comment thread src/components/image-preview-cell.js Outdated
<ImageIcon fontSize="small" />
</IconButton>

<PreviewModal

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PreviewModal is always mounted here — every row in the table keeps a PreviewModal instance in the React tree even when it's never been opened. The project convention (popup-dialog-pattern rule) is to use conditional rendering so dialogs are mounted only when open.

{open && (
  <PreviewModal
    title={itemName || T.translate("preview_modal.title")}
    open
    onClose={() => setOpen(false)}
    url={imageUrl}
    filename={resolvedFileName}
    uploadDate={uploadDate}
  />
)}

MUI Dialog with open=false doesn't add DOM nodes (no keepMounted), so the practical impact is React tree overhead — not stale state — but for large tables this mounts N dialog instances unnecessarily.

@smarcet smarcet left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@priscila-moneo please review comments

Signed-off-by: Priscila Moneo <priscila_moneo@hotmail.com.ar>
@priscila-moneo priscila-moneo requested a review from smarcet June 9, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants